/**
 * @file
 * Video subsystem
 */
#include "c_Engine.h"
#include "assert.h"
#include <malloc.h>

Mtx wiigi::c_Engine::m_GXmodelView2D;

/**
 * Initialises video subsystem
 *
 * @retval d_StatusOK or error
 */
wiigi::e_Status wiigi::c_Engine::initVideo( void )
{
   VIDEO_Init();
   VIDEO_GetPreferredMode( &m_GX_RmodeObj );
   
   /* Widescreen patch by CashMan's Productions (http://www.CashMan-Productions.fr.nf) */
   if (CONF_GetAspectRatio() == CONF_ASPECT_16_9) 
   {
     m_GX_RmodeObj.viWidth = 678; 
     m_GX_RmodeObj.viXOrigin = (VI_MAX_WIDTH_NTSC - 678)/2;
   }
   
   VIDEO_Configure( &m_GX_RmodeObj );
   
   //Allocate noraml frame buffers
   for( u32 Idx = 0; Idx < d_NumberFrameBuffers; Idx++ )
   {
      void* Buffer;
      Buffer = (void*)MEM_K0_TO_K1(SYS_AllocateFramebuffer(&m_GX_RmodeObj));
      assert( 0 != Buffer );
      m_FrameBuffer[ Idx ] = Buffer;
      VIDEO_ClearFrameBuffer(&m_GX_RmodeObj, Buffer, 0x00000000 );
   }
   
   //Allocate frame buffer for console
   if( checkFlags( d_CreateConsole ) )
   {
      m_ConsoleFB = MEM_K0_TO_K1(SYS_AllocateFramebuffer(&m_GX_RmodeObj));
      assert( 0 != m_ConsoleFB );
      VIDEO_ClearFrameBuffer(&m_GX_RmodeObj, m_ConsoleFB, 0x00000000 );
      if( m_ConsoleFB )
      {
         // Initialise the console, required for printf
	      console_init(m_ConsoleFB, 20, 20, m_GX_RmodeObj.fbWidth, m_GX_RmodeObj.xfbHeight, m_GX_RmodeObj.fbWidth*VI_DISPLAY_PIX_SZ);
	   }
	}
   
   if( checkFlags( d_ShowConsole ) )
   {
      VIDEO_SetNextFramebuffer( m_ConsoleFB );
   }
   else
   {
      VIDEO_SetNextFramebuffer( m_FrameBuffer[ m_CurrentFrameBuffer ] );
   }
   
   VIDEO_SetBlack(FALSE);
   VIDEO_Flush();
   VIDEO_WaitVSync();
   if(m_GX_RmodeObj.viTVMode & VI_NON_INTERLACE )
   {
      VIDEO_WaitVSync();
   }
   
   m_GP_FIFO = (u8 *) memalign(32, d_GP_FIFO_Size );
   assert( 0 != m_GP_FIFO );
   memset(m_GP_FIFO, 0, d_GP_FIFO_Size);
   GX_Init(m_GP_FIFO, d_GP_FIFO_Size);
   
   // clears the bg to color and clears the z buffer
   GXColor background = { 0x00, 0x00, 0x00, 0xff };
   GX_SetCopyClear (background, GX_MAX_Z24);
   
   // other gx setup
   f32 yscale;
   u32 xfbHeight;
   yscale = GX_GetYScaleFactor(m_GX_RmodeObj.efbHeight, m_GX_RmodeObj.xfbHeight);
   xfbHeight = GX_SetDispCopyYScale(yscale);
   GX_SetScissor(0, 0, m_GX_RmodeObj.fbWidth, m_GX_RmodeObj.efbHeight);
   GX_SetDispCopySrc(0, 0, m_GX_RmodeObj.fbWidth, m_GX_RmodeObj.efbHeight);
   GX_SetDispCopyDst(m_GX_RmodeObj.fbWidth, xfbHeight);
   GX_SetCopyFilter(m_GX_RmodeObj.aa, m_GX_RmodeObj.sample_pattern, GX_TRUE, m_GX_RmodeObj.vfilter);
   GX_SetFieldMode(m_GX_RmodeObj.field_rendering, ((m_GX_RmodeObj.viHeight==2*m_GX_RmodeObj.xfbHeight)?GX_ENABLE:GX_DISABLE));
   
   if (m_GX_RmodeObj.aa)
     GX_SetPixelFmt(GX_PF_RGB565_Z16, GX_ZC_LINEAR);
   else
     GX_SetPixelFmt(GX_PF_RGB8_Z24, GX_ZC_LINEAR);
   
   GX_SetDispCopyGamma(GX_GM_1_0);
   
   
   // setup the vertex descriptor
   // tells the flipper to expect direct data
   GX_ClearVtxDesc();
   GX_InvVtxCache ();
   GX_InvalidateTexAll();
   
   GX_SetVtxDesc(GX_VA_TEX0, GX_NONE);
   GX_SetVtxDesc(GX_VA_POS, GX_DIRECT);
   GX_SetVtxDesc(GX_VA_CLR0, GX_DIRECT);
   
   
   GX_SetVtxAttrFmt (GX_VTXFMT0, GX_VA_POS, GX_POS_XYZ, GX_F32, 0);
   GX_SetVtxAttrFmt (GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0);
   GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_F32, 0);
   GX_SetZMode (GX_FALSE, GX_LEQUAL, GX_TRUE);
   
   GX_SetNumChans(1);
   GX_SetNumTexGens(1);
   GX_SetTevOp (GX_TEVSTAGE0, GX_PASSCLR);
   GX_SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0);
   GX_SetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY);
   
   guMtxIdentity(m_GXmodelView2D);
   guMtxTransApply (m_GXmodelView2D, m_GXmodelView2D, 0.0F, 0.0F, -50.0F);
   GX_LoadPosMtxImm(m_GXmodelView2D, GX_PNMTX0);
   
   Mtx44 perspective;
   guOrtho(perspective,0, 479, 0, 639, 0, 300.0F);
   GX_LoadProjectionMtx(perspective, GX_ORTHOGRAPHIC);
   
   m_VisibleArea.setValues( 0.0, 0.0, 640.0, 480.0 );
   
   GX_SetViewport(0, 0, m_GX_RmodeObj.fbWidth, m_GX_RmodeObj.efbHeight, 0, 1);
   GX_SetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_CLEAR);
   GX_SetAlphaUpdate(GX_TRUE);
   
   GX_SetCullMode(GX_CULL_NONE);
   
   return d_StatusOK;
}

/**
 * Shuts down the video subsystem
 */
void wiigi::c_Engine::closeVideo( void )
{
   GX_Flush();
   GX_AbortFrame();
   
   for( u32 Idx = 0; Idx < d_NumberFrameBuffers; Idx++ )
   {
      if( m_FrameBuffer[ Idx ] )
      {
         free( m_FrameBuffer[ Idx ] );
         m_FrameBuffer[ Idx ] = 0;
      }
   }
   
   if( m_ConsoleFB )
   {
      free( m_ConsoleFB );
      m_ConsoleFB = 0;
   }
   
   if( m_GP_FIFO )
   {
      free( m_GP_FIFO );
      m_GP_FIFO = 0;
   }   
   
   return;
}

/**
 * Called at end of frame function to update screen contents
 */
void wiigi::c_Engine::updateDisplay( void )
{
   //drawSprites();
   
   GX_DrawDone ();
   
   if( checkFlags( d_ShowConsole ) &&
       m_ConsoleFB                    )
   {
      VIDEO_SetNextFramebuffer( m_ConsoleFB );
      VIDEO_Flush();
   }
   else
   {
      if( ++m_CurrentFrameBuffer == d_NumberFrameBuffers )
      {
         m_CurrentFrameBuffer = 0;
      }
      GX_SetZMode(GX_TRUE, GX_LEQUAL, GX_TRUE);
      GX_SetColorUpdate(GX_TRUE);
      GX_CopyDisp(m_FrameBuffer[ m_CurrentFrameBuffer ], GX_TRUE);
      VIDEO_SetNextFramebuffer(m_FrameBuffer[ m_CurrentFrameBuffer ] );
      VIDEO_Flush();
   }
   
   VIDEO_WaitVSync();
      
   return;
}

/**
 * Allows user to specify background colour that will be used on next frame
 *
 * @param Colour RGBA colour
 */
void wiigi::c_Engine::setBackgroundColour( GXColor& Colour )
{
   GX_SetCopyClear(Colour, GX_MAX_Z24);   
}
